package maps

import (
	"cmp"
	"slices"
)

type MapEntry[K, V any] interface {
	Key() K
	Val() V
}

type Map[K, V any] interface {
	Get(key K) (any, bool)
	Put(key K, val V) bool
	Remove(key K) (any, bool)
	Size() int
	MapEntryItr() Iterator[MapEntry[K, V]]
}

type LinkedHashMap[K cmp.Ordered, V any] struct {
	kv map[K]V
	k  []K
}

func NewLinkedHashMap[K cmp.Ordered, V any](size int) Map[K, V] {
	return &LinkedHashMap[K, V]{
		kv: make(map[K]V, size),
		k:  make([]K, 0, size),
	}
}

func (m *LinkedHashMap[K, V]) Get(key K) (any, bool) {
	v, ok := m.kv[key]
	return v, ok
}

func (m *LinkedHashMap[K, V]) Put(key K, val V) bool {
	if _, ok := m.kv[key]; !ok {
		m.kv[key] = val
		m.k = append(m.k, key)
		return true
	}
	return false
}

func (m *LinkedHashMap[K, V]) Remove(key K) (any, bool) {
	if val, ok := m.kv[key]; ok {
		m.k = deleteKey[K](m.k, key)
		delete(m.kv, key)
		return val, ok
	}
	return nil, false
}

func (m *LinkedHashMap[K, V]) Size() int {
	return len(m.kv)
}

func (m *LinkedHashMap[K, V]) MapEntryItr() Iterator[MapEntry[K, V]] {
	return &mapEntryIterator[K, V]{m: m, i: 0}
}

func (m *LinkedHashMap[K, V]) getKey(index int) K {
	return m.k[index]
}

func deleteKey[K cmp.Ordered](k []K, key K) []K {
	for i := 0; i < len(k); i++ {
		if cmp.Compare[K](k[i], key) == 0 {
			k = slices.Delete(k, i, i+1)
			break
		}
	}
	return k
}

type mapEntryIterator[K cmp.Ordered, V any] struct {
	m *LinkedHashMap[K, V]
	i int
}

func (itr *mapEntryIterator[K, V]) HasNext() bool {
	if itr.i < itr.m.Size() {
		return true
	}
	return false
}

func (itr *mapEntryIterator[K, V]) Next() MapEntry[K, V] {
	index := itr.i
	itr.i = itr.i + 1

	k := itr.m.getKey(index)
	v, _ := itr.m.Get(k)

	return &linkedHashMapEntry[K, V]{key: k, val: v.(V)}
}

type linkedHashMapEntry[K, V any] struct {
	key K
	val V
}

func (l *linkedHashMapEntry[K, V]) Key() K {
	return l.key
}

func (l *linkedHashMapEntry[K, V]) Val() V {
	return l.val
}
